#include <iostream>
#include <unistd.h>
#include <ctime>
#include <cstring>
#include <string>
#include <vector>
#include <unordered_map>
#include <sys/wait.h>
#include <sys/types.h>
#include <cassert>
using std::cout;
using std::endl;
using std::cerr;
using std::vector;
using std::string;
using std::unordered_map;
using std::pair;

typedef void (*functor)();      // typedef 函数指针为 functor

vector<functor> functors;        // 创建函数指针数组, 用来存储函数指针

unordered_map<uint32_t, string> info;       // 用来存储 functors 对应元素存储的任务的信息

typedef pair<pid_t, int> elem;      // elem用来存储 子进程pid 以及对应管道的写入端fd
// first 存储子进程pid, second 存储对应管道写端fd

// 只用函数举例, 不实现具体功能
void f1() {
    cout << "这是一个处理日志的任务, 执行的进程 ID [" << getpid() << "]"
         << "执行时间是[" << time(nullptr) << "]\n" << endl;
    //
}
void f2() {
    cout << "这是一个备份数据任务, 执行的进程 ID [" << getpid() << "]"
         << "执行时间是[" << time(nullptr) << "]\n" << endl;
}
void f3() {
    cout << "这是一个处理网络连接的任务, 执行的进程 ID [" << getpid() << "]"
         << "执行时间是[" << time(nullptr) << "]\n" << endl;
}

void loadFunctor() {
    info.insert({functors.size(), "处理日志"});
    functors.push_back(f1);

    info.insert({functors.size(), "备份数据"});
    functors.push_back(f2);

    info.insert({functors.size(), "处理网络连接"});
    functors.push_back(f3);
}

void childProcWork(int readFd) {
    sleep(1);
    cout << "进程 [" << getpid() << "] 开始工作" << endl;
    
    while (true) {
        uint32_t operatorType = 0;
        ssize_t ret = read(readFd, &operatorType, sizeof(uint32_t));
        if(ret == 0) {
            cout << "父进程任务派完了, 我要走了……" << endl;
            break;
        }
        assert(ret == sizeof(uint32_t));
        (void)ret;

        if (operatorType < functors.size()) {
            functors[operatorType]();
        }
        else {
            cout << "BUG ? operatorType:: " << operatorType << endl;
        }
    }
    cout << "进程 [" << getpid() << "] 结束工作" << endl;
}

void blanceAssignWork(const vector<elem> &processFds) {
    srand((long long)time(nullptr));        // 设置随机数种子
	 
    // 随机对子进程 随机分配任务 num 次
    int cnt = 0;
    int num = 15;
    while (cnt < num) {
        sleep(1);
        // 随机选择子进程
        uint32_t pickProc = rand() % processFds.size();
        // 随机选择任务
        uint32_t pickWork = rand() % functors.size();

        write(processFds[pickProc].second, &pickWork, sizeof(uint32_t));

        cout << "父进程给进程: "  << processFds[pickProc].first << " 派发任务->" << info[pickWork] <<
             ", 对应管道写端fd: " << pickProc << ", 第 " << cnt << " 次派发" << endl;
        
        cnt--;
    }
}

int main() {
    // 0. 加载任务列表
    loadFunctor();

    // 循环创建5个子进程以及对应的管道
    vector<elem> assignMap;         // 子进程pid与对应管道的fd记录 
    int processNum = 5;
    for(int i = 0; i < processNum; i++) {
        int pipeFd[2] = {0};

        if(pipe(pipeFd) != 0) {
            cerr << "第 " << i << " 次, pipe 错误" << endl;
        }

        pid_t id = fork();
        if(id == 0) {
            // 子进程执行代码
            close(pipeFd[1]);

            childProcWork(pipeFd[0]);        // 子进程功能具体函数

            close(pipeFd[0]);
            exit(0);
        }
        // 因为在if(id == 0) 的最后, 执行了 exit(0); 所以子进程不会跳出 if(id == 0) 的内部
        // 所以下面都为父进程执行的代码
        // 父进程执行代码
        close(pipeFd[0]);
        assignMap.push_back(elem(id, pipeFd[1]));     
        // elem(id, pipeFd[1]) 创建pair<uint32_t, uint32_t> 匿名对象, 存储 此次创建子进程pid 和 打开管道的写端fd
        // 并存入 vector 中
    }
    cout << "创建子进程完毕" << endl;
    cout << "父进程, 开始随机给子进程 随机派发任务\n" << endl;

    sleep(1);
    blanceAssignWork(assignMap);        // 父进程派发任务函数


    // 回收所有子进程
    for(int i = 0; i < processNum; i++) 
        close(assignMap[i].second);
    
    for(int i = 0; i < processNum; i++)  {
        if(waitpid(assignMap[i].first, nullptr, 0)) {
            cout << "等待子进程_pid: " << assignMap[i].first << ", 等待成功. Number: " << i << endl;
        }
    }

    return 0;
}
